package org.rency.common.trigger.service.impl;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.quartz.CronExpression;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerKey;
import org.quartz.impl.JobDetailImpl;
import org.rency.common.trigger.beans.TaskStatusEnum;
import org.rency.common.trigger.exception.DaemonSchedulerException;
import org.rency.common.trigger.schedule.DaemonCronTriggerFactoryBean;
import org.rency.common.trigger.schedule.DaemonMethodInvokingJobDetailFactoryBean;
import org.rency.common.trigger.schedule.DaemonTriggerBean;
import org.rency.common.trigger.schedule.repository.DaemonTaskTriggerDO;
import org.rency.common.trigger.schedule.repository.DaemonTaskTriggerRepository;
import org.rency.common.trigger.service.ScheduleService;
import org.rency.common.trigger.util.DaemonTriggerConverter;
import org.rency.common.trigger.util.TriggerDict;
import org.rency.common.utils.enums.YesNoEnum;
import org.rency.common.utils.tool.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;

@Component("scheduleService")
public class ScheduleServiceImpl implements ScheduleService{
	
	private static final Logger logger = LoggerFactory.getLogger(ScheduleServiceImpl.class);
	
	private boolean available;
	
	@Resource(name="daemonTaskTriggerRepository")
	private DaemonTaskTriggerRepository daemonTaskTriggerRepository;

	@Resource(name="daemonScheduler")
    private Scheduler daemonScheduler;
	
	@Resource(name="daemonTransactionTemplate")
	private TransactionTemplate daemonTransactionTemplate;
	
	@Override
	public List<Trigger> loadEnableTriggers() throws DaemonSchedulerException {
		TaskStatusEnum status = TaskStatusEnum.NORMAL;
		List<DaemonTriggerBean> triggerBeanList = DaemonTriggerConverter.convert2DaemonTriggerBean(daemonTaskTriggerRepository.loadTriggerByStatus(status));
		if(CollectionUtils.isEmpty(triggerBeanList)){
			logger.info("未加载到[{}]状态的定时任务[{}].",status.getCode(),TriggerDict.DAEMON_SCHEDULER_NAME);
			return new ArrayList<Trigger>();
		}
		logger.info("加载[{}]队列定时任务[{}]个.",TriggerDict.DAEMON_SCHEDULER_NAME,triggerBeanList.size());
		List<Trigger> triggerList = new ArrayList<Trigger>();
		for(DaemonTriggerBean bean : triggerBeanList){
			DaemonCronTriggerFactoryBean daemonCronTriggerFactoryBean = null;
			try{
				daemonCronTriggerFactoryBean = getTriggerBean(bean);
			}catch(Exception e){
				continue;
			}
			if(daemonCronTriggerFactoryBean != null){
				triggerList.add(daemonCronTriggerFactoryBean.getObject());
				logger.info("装载[{}]任务",bean.getBeanName());
			}
		}
		logger.info("成功装载[{}]队列定时任务[{}]个.",TriggerDict.DAEMON_SCHEDULER_NAME,triggerList.size());
		return triggerList;
	}

	@Override
	public void initDataBaseTable() {
		daemonTaskTriggerRepository.createTable();
	}

	@Override
	public boolean pauseAllTriggers() throws DaemonSchedulerException{
		logger.info("停止[{}]中所有任务.",TriggerDict.DAEMON_SCHEDULER_NAME);
		try{
			daemonScheduler.pauseAll();
		}catch(SchedulerException e){
			logger.error("停止["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中所有任务异常.",e);
			throw new DaemonSchedulerException("停止["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中所有任务异常.",e);
		}
		return true;
	}

	@Override
	public boolean pauseTriggers(Integer triggerId) throws DaemonSchedulerException {
		logger.info("停止["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中任务[{}].",triggerId);
		DaemonTaskTriggerDO entity = daemonTaskTriggerRepository.find(triggerId);
		if(entity == null){
			logger.error("停止["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中任务失败,任务不存在[{}].",triggerId);
			return false;
		}
		return pauseTriggers(entity.getBeanName());
	}

	@Override
	public boolean pauseTriggers(String beanName) throws DaemonSchedulerException {
		logger.info("停止["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中任务[{}].",beanName);
		DaemonTaskTriggerDO entity = daemonTaskTriggerRepository.find(beanName);
		if(entity == null){
			logger.error("停止["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中任务失败,任务不存在[{}].",beanName);
			return false;
		}
		try {
			daemonScheduler.pauseTrigger(TriggerKey.triggerKey(TriggerDict.DAEMON_TRIGGER_NAME+beanName, TriggerDict.DAEMON_GROUP_NAME));
			return true;
		} catch (SchedulerException e) {
			logger.error("停止["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中任务[{}]异常.",beanName,e);
			throw new DaemonSchedulerException("停止["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中任务["+beanName+"]异常.",e);
		}
	}

	@Override
	public boolean resumeAllTriggers() throws DaemonSchedulerException {
		logger.info("恢复["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中所有任务.");
		try {
			daemonScheduler.resumeAll();
			return true;
		} catch (SchedulerException e) {
			logger.error("恢复["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中所有任务异常.",e);
			throw new DaemonSchedulerException("恢复["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中所有任务异常.",e);
		}
	}

	@Override
	public boolean resumeTriggers(Integer triggerId) throws DaemonSchedulerException {
		logger.info("恢复["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中所有任务[{}].",triggerId);
		DaemonTaskTriggerDO entity = daemonTaskTriggerRepository.find(triggerId);
		if(entity == null){
			logger.error("恢复["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中任务失败,任务不存在[{}].",triggerId);
			return false;
		}
		return resumeTriggers(entity.getBeanName());
	}

	@Override
	public boolean resumeTriggers(String beanName) throws DaemonSchedulerException {
		logger.info("恢复["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中所有任务[{}].",beanName);
		DaemonTaskTriggerDO entity = daemonTaskTriggerRepository.find(beanName);
		if(entity == null){
			logger.error("恢复["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中任务失败,任务不存在[{}].",beanName);
			return false;
		}
		try {
			TriggerKey triggerKey = TriggerKey.triggerKey(TriggerDict.DAEMON_TRIGGER_NAME+beanName, TriggerDict.DAEMON_GROUP_NAME);
			boolean isExists = daemonScheduler.checkExists(triggerKey);
			if(!isExists){
				DaemonCronTriggerFactoryBean daemonCronTriggerFactoryBean = null;
				try{
					DaemonTriggerBean bean = DaemonTriggerConverter.convert2DaemonTriggerBean(entity);
					daemonCronTriggerFactoryBean = getTriggerBean(bean);
				}catch(Exception e){
					logger.error("恢复["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中的任务[{}]异常.",beanName,e);
					throw new DaemonSchedulerException("恢复["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中的任务["+beanName+"]异常.",e);
				}
				if(daemonCronTriggerFactoryBean != null){
					daemonScheduler.rescheduleJob(triggerKey,daemonCronTriggerFactoryBean.getObject());
					if(TaskStatusEnum.get(entity.getStatus()) != null){
						daemonTaskTriggerRepository.updateStatusByName(beanName, TaskStatusEnum.NORMAL,TaskStatusEnum.get(entity.getStatus()));
					}
				}
			}else{
				daemonScheduler.resumeTrigger(TriggerKey.triggerKey(TriggerDict.DAEMON_TRIGGER_NAME+beanName, TriggerDict.DAEMON_GROUP_NAME));
			}
			return fireTrigger(beanName);
		} catch (SchedulerException e) {
			logger.error("恢复["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中的任务[{}]异常.",beanName,e);
			throw new DaemonSchedulerException("恢复["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中的任务["+beanName+"]异常.",e);
		}
	}
	
	/**
	 * 获取Trigger对象
	 * </br>Created on 2015年12月3日
	 * @param bean
	 * @return
	 * @throws Exception
	 */
	private DaemonCronTriggerFactoryBean getTriggerBean(DaemonTriggerBean bean) throws Exception{
		JobDetail jobDetail = getJobDetail(bean);
		DaemonCronTriggerFactoryBean daemonCronTriggerFactoryBean = new DaemonCronTriggerFactoryBean(jobDetail, bean);
		return daemonCronTriggerFactoryBean;
	}
	
	/**
	 * 获取JobDetail对象
	 * </br>Created on 2015年12月3日
	 * @param bean
	 * @return
	 * @throws Exception
	 */
	private JobDetail getJobDetail(DaemonTriggerBean bean) throws Exception{
		DaemonMethodInvokingJobDetailFactoryBean daemonJobDetailFactoryBean = null;
		try{
			daemonJobDetailFactoryBean = new DaemonMethodInvokingJobDetailFactoryBean(bean);
		}catch(Exception e){
			logger.error("[{}]队列中定时任务未找到[{}].",TriggerDict.DAEMON_SCHEDULER_NAME,daemonJobDetailFactoryBean.getTargetClass().getName()+".{}().",daemonJobDetailFactoryBean.getTargetMethod());
			throw new Exception(e);
		}
		JobDetailImpl jobDetail = (JobDetailImpl) daemonJobDetailFactoryBean.getJobDetail();
		jobDetail.setDescription(bean.getDescription());
		jobDetail.setName(bean.getBeanName());
		return jobDetail;
	}
	
	@Override
	public boolean modifyCronExpression(final String beanName, final String cronExpression) throws DaemonSchedulerException {
		logger.info("修改["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中任务[{}]的执行计划[{}].",beanName,cronExpression);
		Assert.notNull(beanName, "任务目标不能为空");
		Assert.notNull(cronExpression, "表达式不能为空");
		try {
            new CronExpression(cronExpression);
        } catch (ParseException e) {
            throw new IllegalArgumentException("表达式不符合规范");
        }
		return daemonTransactionTemplate.execute(new TransactionCallback<Boolean>() {
			@Override
			public Boolean doInTransaction(TransactionStatus status) {
				try{
					DaemonTaskTriggerDO entity = daemonTaskTriggerRepository.find(beanName);
					if(entity == null){
						logger.error("更新任务执行计划失败,队列中[{}]任务[{}]不存在.",TriggerDict.DAEMON_SCHEDULER_NAME,beanName);
						return false;
					}
					
					boolean updateResult = daemonTaskTriggerRepository.updateCronExpression(beanName, cronExpression);
					if(!updateResult){
						logger.info("更新任务[{}]执行计划[{}]失败,更新重试1.",beanName,cronExpression);
						updateResult = daemonTaskTriggerRepository.updateCronExpression(beanName, cronExpression);
						if(!updateResult){
							logger.error("更新任务[{}]执行计划[{}]失败",beanName,cronExpression);
							return false;
						}
					}
					TriggerKey triggerKey = TriggerKey.triggerKey(TriggerDict.DAEMON_TRIGGER_NAME+beanName, TriggerDict.DAEMON_GROUP_NAME);
					entity.setCronExpression(cronExpression);
					DaemonTriggerBean triggerBean = DaemonTriggerConverter.convert2DaemonTriggerBean(entity);
					DaemonCronTriggerFactoryBean daemonCronTriggerFactoryBean = getTriggerBean(triggerBean);
					daemonScheduler.rescheduleJob(triggerKey,daemonCronTriggerFactoryBean.getObject());
					return true;
				}catch(Exception e){
					logger.error("更新任务[{}]执行计划[{}]异常.",beanName,cronExpression,e);
					status.setRollbackOnly();
					return false;
				}
			}
		});
	}
	
	public boolean fireTrigger(String beanName) throws DaemonSchedulerException{
		logger.info("立即触发["+TriggerDict.DAEMON_SCHEDULER_NAME+"]中任务[{}].",beanName);
		Assert.notNull(beanName, "任务目标不能为空");
		try{
			DaemonTaskTriggerDO entity = daemonTaskTriggerRepository.find(beanName);
			if(entity == null){
				logger.error("触发任务失败,队列中[{}]任务[{}]不存在.",TriggerDict.DAEMON_SCHEDULER_NAME,beanName);
				return false;
			}
			if(TaskStatusEnum.get(entity.getStatus()) != TaskStatusEnum.NORMAL){
				logger.info("[{}]任务触发失败,任务状态不正确,当前状态[{}],应为[{}].",beanName,entity.getStatus(),TaskStatusEnum.NORMAL.getCode());
				return false;
			}
			TriggerKey triggerKey = TriggerKey.triggerKey(TriggerDict.DAEMON_TRIGGER_NAME+beanName, TriggerDict.DAEMON_GROUP_NAME);
			entity.setNextFire(new Date());
			DaemonTriggerBean triggerBean = DaemonTriggerConverter.convert2DaemonTriggerBean(entity);
			DaemonCronTriggerFactoryBean daemonCronTriggerFactoryBean = getTriggerBean(triggerBean);
			daemonScheduler.rescheduleJob(triggerKey,daemonCronTriggerFactoryBean.getObject());
			return true;
		}catch(Exception e){
			logger.error("立即触发任务[{}]异常.",beanName,e);
			return false;
		}
	}

	@Override
	public boolean addTrigger(final String beanName, final String description, final String cronExpression,final YesNoEnum concurrent, final String memo)
			throws DaemonSchedulerException {
		logger.info("在["+TriggerDict.DAEMON_SCHEDULER_NAME+"]队列中添加新任务[{},{},{}].",beanName,description,cronExpression);
		Assert.notNull(beanName, "任务目标不能为空");
		Assert.notNull(description, "任务描述不能为空");
		Assert.notNull(cronExpression, "表达式不能为空");
		
		return daemonTransactionTemplate.execute(new TransactionCallback<Boolean>() {
			@Override
			public Boolean doInTransaction(TransactionStatus status) {
				try{
					if(daemonTaskTriggerRepository.find(beanName) != null){
						logger.info("队列[{}]中任务[{}]已存在.",TriggerDict.DAEMON_SCHEDULER_NAME,beanName);
						return false;
					}
					DaemonTaskTriggerDO entity = new DaemonTaskTriggerDO();
					entity.setBeanName(beanName);
					entity.setConcurrentTag(concurrent == null ? YesNoEnum.NO.getCode() : concurrent.getCode());
					entity.setCronExpression(cronExpression);
					entity.setDescription(description);
					entity.setMemo(memo);
					entity.setStatus(TaskStatusEnum.NORMAL.getCode());
					
					if(!daemonTaskTriggerRepository.save(entity)){
						logger.info("队列[{}]中添加任务失败,保存重试1.",entity);
						if(!daemonTaskTriggerRepository.save(entity)){
							logger.error("队列[{}]中添加任务失败.",entity);
							return false;
						}
					}
					
					DaemonTriggerBean triggerBean = DaemonTriggerConverter.convert2DaemonTriggerBean(entity);
					DaemonCronTriggerFactoryBean daemonCronTriggerFactoryBean = getTriggerBean(triggerBean);
					daemonScheduler.scheduleJob(daemonCronTriggerFactoryBean.getObject());
					return true;
				}catch(Exception e){
					logger.error("更新任务[{}]执行计划[{}]异常.",beanName,cronExpression,e);
					status.setRollbackOnly();
					return false;
				}
			}
		});
	}

	@Override
	public boolean removeTrigger(final String beanName) throws DaemonSchedulerException {
		logger.info("移除["+TriggerDict.DAEMON_SCHEDULER_NAME+"]队列中的任务[{}].",beanName);
		return daemonTransactionTemplate.execute(new TransactionCallback<Boolean>() {
			@Override
			public Boolean doInTransaction(TransactionStatus status) {
				try{
					if(daemonTaskTriggerRepository.find(beanName) == null){
						logger.info("队列[{}]中任务[{}]不存在.",TriggerDict.DAEMON_SCHEDULER_NAME,beanName);
						return false;
					}
					pauseTriggers(beanName);
					daemonScheduler.unscheduleJob(TriggerKey.triggerKey(TriggerDict.DAEMON_TRIGGER_NAME+beanName, TriggerDict.DAEMON_GROUP_NAME));
					
					return true;
				}catch(Exception e){
					logger.error("移除[{}]队列中的任务[{}]异常.",TriggerDict.DAEMON_SCHEDULER_NAME,beanName,e);
					status.setRollbackOnly();
					return false;
				}
			}
		});
	}

	@Override
	public boolean isAvailable() {
		return this.available;
	}

	@Override
	public void setSchedulerAvailable(boolean available) {
		this.available = available;
	}
	
	@Override
	public boolean triggerException(Integer triggerId) throws DaemonSchedulerException {
		this.pauseTriggers(triggerId);
		daemonTaskTriggerRepository.updateStatusById(triggerId, TaskStatusEnum.EXCEPTION, TaskStatusEnum.PROCESS);
		return false;
	}

	public List<String> parseCronExpression(String cronExpression,int count) throws ParseException{
		CronExpression cron = null;
		try {
			cron = new CronExpression(cronExpression);
        } catch (ParseException e) {
            throw e;
        }
		count = count == 0 ? 1 : count; 
		List<String> resultList = new ArrayList<String>();
		Date date = new Date();
		while(true){
			date = cron.getNextValidTimeAfter(date);
			resultList.add(DateUtils.parseDate(date, DateUtils.PATTERN_FULLDATETIME));
			count --;
			if(count == 0){
				break;
			}
		}
		return resultList;
	}
	
}
